#!/usr/local/bin/perl -w
# Hirotaka Ogawa (hirotaka.ogawa at gmail.com)
#
# DERIVED FROM:
# georss-gmaps.cgi - Maps geocoded RSS to Gmaps
# Tatsuhiko Miyagawa <miyagawa@bulknews.net>
# License: Same as Perl (Artistic + GPL2)

use strict;
use CGI;
use Encode;
use XML::RSS;
use LWP::UserAgent;
use Template;
use WWW::Blog::Metadata;

use constant NS_GEOURL => "http://geourl.org/rss/module/";
use constant NS_GEORDF => "http://www.w3.org/2003/01/geo/wgs84_pos#";

our $version = 0.10;

my $q = CGI->new;
$q->charset('utf-8');
binmode STDOUT, ":utf8";

show_src($q) if $q->param('src');
show_html($q) unless $q->param('url');
do_task($q);

sub show_src {
    my $q = shift;
    print $q->header('text/plain');
    open my $fh, $0 or die $!;
    print while <$fh>;
    close $fh;
    exit;
}

sub do_task {
    my $q = shift;
    my $u = $q->param('url');
    my $feed;
    if ($u) {
        $feed = fetch_feed($u);
    }
    print $q->header($q->param('xml') ? 'text/xml' : 'application/vnd.google-earth.kml+xml');
    my $tmpl = get_tmpl();
    my $tt = Template->new;
    $tt->process(\$tmpl, { q => $q, feed => $feed }) or print $tt->error;
}

sub fetch_feed {
    my $url = shift or return;
    my $ua  = LWP::UserAgent->new;
    $ua->agent("geofeed-gmaps/$version");
    $ua->timeout(10);
    my $res = $ua->get($url);
    $res->is_success or return;
    my $content = $res->content;
    my $type    = $res->content_type;
    if ($type =~ m!(rdf|rss)! || $content =~ /<rss/ || ($type =~ /xml/ && $content =~ /<rdf:RDF/)) {
        return parse_rss($res->content);
    } elsif ($type =~ m!atom! || $content =~ /<feed ver/) {
        return parse_atom($res->content);
    } else {
        my $meta = WWW::Blog::Metadata->extract_from_html(\$res->content, $url);
        return fetch_feed($meta->feeds->[0]);
    }
}

sub parse_rss {
    my $xml = shift;
    my $rss = XML::RSS->new;
    $rss->add_module(prefix => "geourl", uri => NS_GEOURL);
    $rss->add_module(prefix => "geo", uri => NS_GEORDF);
    $rss->add_module(prefix => "content", uri => "http://purl.org/rss/1.0/modules/content/");
    $rss->parse($xml);

    for my $item (@{$rss->items}) {
        $item->{g} = $item->{geo} ? { latitude => $item->{geo}->{lat},
                                      longitude => $item->{geo}->{long} } : $item->{geourl};
    }

    return $rss;
}

sub parse_atom {
    my $xml = shift;
    my $atom = XML::Atom::Feed->new(\$xml);

    no warnings;
    ## XML::RSS interface compat.
    *XML::Atom::Feed::items = \&XML::Atom::Feed::entries;
    *XML::Atom::Entry::g = sub {
        my $entry = shift;
        if ($entry->get(NS_GEOURL, "latitude")) {
            return { latitude => $entry->get(NS_GEOURL, "latitude"),
                     longitude => $entry->get(NS_GEOURL, "longitude") };
        } elsif ($entry->get(NS_GEORDF, "lat")) {
            return { latitude => $entry->get(NS_GEORDF, "lat"),
                     longitude => $entry->get(NS_GEORDF, "long") };
        }
    };
    use warnings;

    return $atom;
}

sub get_tmpl {
    return << 'TMPL';
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<Folder>
<name>[% (feed.channel.title || feed.title) | html %]</name>
[% desc = feed.channel.description %]
[% link = feed.channel.link %]
[% IF desc %]
<description><![CDATA[[% desc %] [%- IF link %](<a href="[% link %]">link</a>)[%- END %]]]></description>
[% END %]
[% FOREACH i = feed.items %]
[%- IF i.g.longitude %]
<Placemark>
<name>[% i.title | html %]</name>
<description><![CDATA[[% (i.content.encoded || i.description || i.dc.description) %] (<a href="[% i.link %]">read more</a>)]]></description>
<LookAt>
<longitude>[% i.g.longitude %]</longitude>
<latitude>[% i.g.latitude %]</latitude>
<range>500.00</range>
</LookAt>
<Point>
<coordinates>[% i.g.longitude %],[% i.g.latitude %],0</coordinates>
</Point>
</Placemark>
[%- END %]
[% END %]
</Folder>
</kml>
TMPL
}

sub show_html {
    my $q = shift;
    print $q->header('text/html');
    print <<HTML;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>georss2kml.cgi: Maps the GeoRSS onto Google Earth</title>
    <style>h1 { font-size: 14px; } body, input { font-size: 12px; font-family: verdana,arial,sans-serif; } .footer { font-size: 10px; padding-top: 10px; }</style>
  </head>
  <body>
    <h1>Maps the GeoRSS onto Google Earth <small>(SEE ALSO <a href="http://as-is.net/blog/archives/001031.html">http://as-is.net/blog/archives/001031.html</a>)</small></h1>

    <p>This script converts your GeoRSS into a Google Earth KML file.  You can use it as a static list, as well as a Google Earth Network Link.</p>

    <form action="georss2kml.cgi">
    <p>
    GeoRSS URL:
    <input type="text" name="url" size="50" value="" />
    <input type="submit" value="Generate KML" /><br />
    Output\'s Content-Type:
    <input type="radio" name="xml" value="0" checked="checked" />application/vnd.google-earth.kml+xml
    <input type="radio" name="xml" value="1" />text/xml<br />
    </p>
    </form>
    <p>Example: <a href="http://as-is.net/maps/georss2kml.cgi?url=http%3A%2F%2Fkanzaki.com%2Fworks%2F2004%2Fimgdsc%2Ftokyo-ls.rdf">1</a>, <a href="georss2kml.cgi?url=http%3A%2F%2Fgeourl.org%2Fnear%2F%3Fp%3Dhttp%3A%2F%2Fwww.ldodds.com%2Fblog%2F%26format%3Drss10">2</a></p>
    <p class="footer">2005, <a href="http://as-is.net/blgo/">Hirotaka Ogawa</a><br />This script is a derived work of <a href="http://blog.bulknews.net/georss-gmaps.cgi">Map geocoded RSS/Atom to Google Maps</a> written by <a href="http://bulknews.typepad.com/">Tatsuhiko Miyagawa</a>.</p>
  </body>
</html>
HTML
    exit;
}
